﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

using Seven.Member.Common;
using Seven.Member.Internal;
using Seven.Tools;

namespace Seven.Member
{
    /// <summary>
    /// 表示身份验证服务的 Token 对象。
    /// </summary>
    public class MemberToken : IEquatable<MemberToken>, IEquatable<Guid>, IEquatable<string>, INotifyPropertyChanged
    {
        private string _account;
        private string _imei;
        private Guid? _guid;

        private UserInfoModel _user;
        private MemberTokenCollection _tokensPool;
        private static MemberTokenCollection _tokens;

        private DateTime? _loginTime;
        private DateTime? _logoutTime;
        private DateTime? _lastRequestTime;

        private const string _LoginTimeName = "LoginTime";
        private const string _LogoutTimeName = "LogoutTime";
        private const string _LastRequestTimeName = "LastRequestTime";

        internal bool disablePropertyChangedEvent = false;

        #region 构造函数定义

        /// <summary>
        /// 初始化类型 <see cref="MemberToken"/> 的新实例。
        /// </summary>
        /// <param name="tokensPoolSelector"></param>
        private MemberToken(Func<MemberTokenCollection> tokensPoolSelector)
        {
            Check.NotNull(tokensPoolSelector);
            this._tokensPool = tokensPoolSelector();
        }

        /// <summary>
        /// 以指定的字符串值作为登录身份用户编码初始化类型 <see cref="MemberToken"/> 的新实例。
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        private MemberToken(string account)
            : this(GetTokensPool)
        {
            this._account = account != null ? account.Trim() : string.Empty;
            this.InitUser();
        }

        /// <summary>
        /// 以指定的字符串值作为登录身份用户编码和移动设备编码初始化类型 <see cref="MemberToken"/> 的新实例。
        /// <para>当由移动设备发起登录请求时调用该构造函数创建登录身份验证请求。</para>
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <param name="imei">移动设备硬件编号</param>
        private MemberToken(string account, string imei)
            : this(GetTokensPool)
        {
            this._account = account != null ? account.Trim() : string.Empty;
            this.InitUser();

            imei = imei != null ? imei.Trim() : string.Empty;
            Check.NotEmpty(imei);
            this._imei = imei;
        }

        #endregion

        #region internal 静态属性定义 - 获取登录/在线用户列表

        /// <summary>
        /// 获取所有的在线用户列表。
        /// </summary>
        internal static MemberTokenCollection Tokens
        {
            get
            {
                if (_tokens == null)
                { _tokens = new MemberTokenCollection(); }

                return _tokens;
            }
        }

        #endregion

        #region 公共属性定义 - 用户基本信息

        /// <summary>
        /// 获取该登录用户的用户ID。如果当前登录身份无效，则返回 0。
        /// </summary>
        public int UserID
        {
            get { return this._user != null ? this._user.UserID : 0; }
        }

        /// <summary>
        /// 获取该登录用户的登录帐号。如果当前登录身份无效，则返回 null。
        /// </summary>
        public string Account
        {
            get { return this._user != null ? this._user.Account : null; }
        }

        /// <summary>
        /// 获取该登录用户的用户名。如果当前登录身份无效，则返回 null。
        /// </summary>
        public string UserName
        {
            get { return this._user != null ? this._user.UserName : null; }
        }

        /// <summary>
        /// 获取该登录用户的用户别。如果当前登录身份无效，则返回 null。
        /// </summary>
        public string LoginCode
        {
            get { return this._user != null ? this._user.LoginCode : null; }
        }

        #endregion

        #region 公共属性定义 - 登录身份标识信息

        /// <summary>
        /// 获取当前登录身份标识对象用于存储在线用户列表的对象池。
        /// </summary>
        internal MemberTokenCollection TokensPool
        {
            get { return this._tokensPool; }
        }

        /// <summary>
        /// 获取当前 <see cref="MemberToken"/> 对象的 Guid 值，表示该登录身份的唯一标识。
        /// </summary>
        public Guid Guid
        {
            get
            {
                if (!this._guid.HasValue)
                { this._guid = Guid.NewGuid(); }

                return this._guid.Value;
            }
        }

        /// <summary>
        /// 返回一个 bool 值，表示当前对象是否是一个有效的用户登录身份。
        /// 即初始化该对象的 user 是否有效。
        /// </summary>
        public bool IsValidUser
        {
            get { return this._user != null; }
        }

        /// <summary>
        /// 获取使用该身份登录的移动设备国际身份码，如果该次登录不是由移动设备登录，则该属性返回 null。
        /// </summary>
        public string IMEI
        {
            get { return this._imei; }
        }

        /// <summary>
        /// 返回一个 bool 值，表示当前对象中包含的 IMEI 是否是一个有效的 移动设备国际身份硬件编号。
        /// 即初始化该对象的 imei 是否有效。
        /// </summary>
        public bool IsValidIMEI
        {
            get { return !string.IsNullOrWhiteSpace(this.IMEI); }
        }

        /// <summary>
        /// 返回一个 bool 值，表示当前登录身份对象是否表示由 移动设备（手机、平板等）发起的登录。
        /// </summary>
        public bool IsPhoneLogin
        {
            get { return !string.IsNullOrWhiteSpace(this.IMEI); }
        }

        /// <summary>
        /// 获取表示当前登录身份的用户模型对象。
        /// <para>如果当前登录身份无效，则返回 null。</para>
        /// </summary>
        internal UserInfoModel User
        {
            get { return this._user; }
        }

        #endregion

        #region 公共属性定义 - 登录状态相关属性

        /// <summary>
        /// 获取一个 bool 值，表示该登录身份是否已过期（登录超时）。
        /// </summary>
        public bool IsTimeout
        {
            get
            {
                if (!this.LastRequestTime.HasValue)
                { return false; }

                int timeout = Config.SessionTimeout;
                return timeout == 0
                    ? true
                    : (DateTime.Now - this.LastRequestTime.Value).TotalMinutes > timeout;
            }
        }

        /// <summary>
        /// 获取一个 <see cref="LoginState"/> 枚举值，表示该用户标识的登录状态。
        /// </summary>
        public LoginState LoginState
        {
            get
            {
                MemberToken token = this.TokensPool.GetTokenByGuid(this.Guid);
                return this.ValidateLoginState() && token != null
                    ? LoginState.Online
                    : LoginState.Offline;
            }
        }

        /// <summary>
        /// 获取一个 System.Nullable&lt;DateTime&gt; 类型值，表示该登录用户的登录时间。
        /// <para>如果该属性没有返回值，则表示该身份尚未登录。</para>
        /// </summary>
        public DateTime? LoginTime
        {
            get { return this._loginTime; }
            internal set
            {
                this._loginTime = value;
                this.OnPropertyChanged(_LastRequestTimeName);
            }
        }

        /// <summary>
        /// 获取一个 System.Nullable&lt;DateTime&gt; 类型值，表示该登录用户的登出时间。
        /// <para>如果该属性没有返回值，则表示该身份尚未执行登出操作。</para>
        /// </summary>
        public DateTime? LogoutTime
        {
            get { return this._logoutTime; }
            internal set
            {
                this._logoutTime = value;
                this.OnPropertyChanged(_LastRequestTimeName);
            }
        }

        /// <summary>
        /// 获取一个 System.Nullable&lt;DateTime&gt; 类型值，表示该登录用户最后一次提交请求到服务器的时间。
        /// <para>如果该属性没有返回值，则表示该登录标识所标识的用户还未提交请求值服务器。</para>
        /// </summary>
        public DateTime? LastRequestTime
        {
            get { return this._lastRequestTime; }
            internal set
            {
                this._lastRequestTime = value;
                this.OnPropertyChanged(_LastRequestTimeName);
            }
        }

        #endregion

        #region 公共方法定义 - 验证登录信息和状态

        /// <summary>
        /// 验证指定的登录帐号（用户名或登录别名）和密码是否匹配。
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <param name="password">密码明文</param>
        /// <returns></returns>
        internal static bool ValidatePassword(string account, string password)
        {
            UserInfoModel user = GetMemberUser(account);

            return ValidatePassword(user, password);
        }

        /// <summary>
        /// 验证指定的用户对象和密码是否匹配。
        /// </summary>
        /// <param name="user">用户数据模型</param>
        /// <param name="password">密码明文</param>
        /// <returns></returns>
        private static bool ValidatePassword(UserInfoModel user, string password)
        {
            if (user == null)
            { return false; }

            if (string.IsNullOrEmpty(password) && string.IsNullOrEmpty(user.Password))
            { return true; }

            string ciphertext = new Seven.Cryptography.Encrypt().Encode(password);
            return (user.Password ?? string.Empty).Equals(ciphertext, StringComparison.InvariantCultureIgnoreCase);
        }

        internal bool ValidateLoginState()
        {
            return this.LoginTime.HasValue && !this.IsTimeout && !this.LogoutTime.HasValue;
        }

        private bool ValidatePhoneState(string imei, string account)
        {
            return true;
        }

        #endregion

        #region 公共方法定义 - 登录和注销登录相关操作

        /// <summary>
        /// 执行系统登录操作。
        /// </summary>
        /// <param name="password">密码明文</param>
        /// <returns></returns>
        internal LoginResult Login(string password)
        {
            LoginResult ret = this.InternalLogin(password);
            return ret;
        }

        /// <summary>
        /// 执行注销登录操作。
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// 该操作将判断如果当前身份尚未登录，则返回 LogoutResult.InvalidLoginStatus；
        /// 否则将当前身份的 LogoutTime 属性设置为当前时间；并将当前对象从在线用户列表中移除。
        /// </remarks>
        internal LogoutResult Logout()
        {
            LogoutResult ret = this.InternalLogout();
            return ret;
        }

        /// <summary>
        /// 超时自动注销登录时指定的操作。
        /// </summary>
        internal void TimeoutLogout()
        {
            this.InternalRealLogout();
        }

        /// <summary>
        /// 刷新用户身份标识的最后一次提交请求到服务器的时间。
        /// </summary>
        internal void UpdateLastRequestTime()
        {
            this.LastRequestTime = DateTime.Now;
        }

        #endregion

        #region 私有方法定义 - 初始化基础对象

        private void InitUser()
        {
            Check.NotEmpty(this._account);
            this._user = GetMemberUser(this._account);
        }

        /// <summary>
        /// 根据登录帐号获取对应的登录信息
        /// </summary>
        /// <param name="account">登录时所用的帐号（用户名或者登录别名）</param>
        /// <returns></returns>
        private static UserInfoModel GetMemberUser(string account)
        {
            var user = Seven.Service.ServiceFactory.SysUser.GetUserByAccount(account);

            if (user == null) { return null; }
            else
            {
                var model = new UserInfoModel();
                model.UserID = user.ID;
                model.Account = account;
                model.UserName = user.UserName;
                model.LoginCode = user.LoginCode;
                model.Password = user.PassWord;
                model.RealName = user.Staff.RealName;
                model.RoleID = user.RoleID;
                model.GroupID = user.Staff.GroupID;
                model.CompanyID = user.Staff.CompanyID;
                model.DeptID = user.Staff.DeptID;

                return model;
            }
        }

        #endregion

        #region 私有方法定义 - 登录和注销登录操作内部方法

        private LoginResult InternalLogin(string password)
        {
            // 判断用户编号有效性
            if (!this.IsValidUser)
            { return LoginResult.InvalidAccount; }

            // 判断密码有效性
            if (!ValidatePassword(this.User, password))
            { return LoginResult.InvalidPassword; }

            // 判断当前用户是否已经登录
            if (IsOnline(this.Account) && !Config.EnableMultiDeciveOnline)
            { return LoginResult.InvalidLoginStatus; }

            // 判断当由移动设备登录时的处理情况
            if (this.IsPhoneLogin)
            {
                // 判断移动设备是否存在且有效
                if (!this.IsValidIMEI)
                { return LoginResult.InvalidDevice; }

                // 判断移动设备使用状态
                if (!this.ValidatePhoneState(this.IMEI, this.Account))
                { return LoginResult.InvalidDeviceStatus; }

                // 判断设备是否已经被其他用户登录，同上一句判断，双重校验
                MemberToken token = GetTokenByIMEI(this.IMEI);
                if (token != null && token.LoginState == LoginState.Online)
                { return LoginResult.InvalidDeviceStatus; }
            }

            if (!Config.EnableMultiDeciveOnline)
            {
                this.TokensPool.RemoveByAccount(this.Account);
                if (this.IsPhoneLogin)
                { this.TokensPool.RemoveByIMEI(this.IMEI); }
            }

            this.TokensPool.Add(this.Guid, this);

            this.LoginTime = DateTime.Now;
            this.LastRequestTime = DateTime.Now;
            // 将新的Token值赋值给UserModel
            this.User.Token = this.Guid;

            return LoginResult.Success;
        }

        private LogoutResult InternalLogout()
        {
            if (this.LoginState != LoginState.Online)
            { return LogoutResult.InvalidLoginStatus; }

            this.InternalRealLogout();
            return LogoutResult.Success;
        }

        private void InternalRealLogout()
        {
            this.LogoutTime = DateTime.Now;

            this.TokensPool.RemoveByGuid(this.Guid);
        }

        #endregion

        #region internal 静态方法定义 - 登录和注销登录相关操作

        /// <summary>
        /// 身份验证登录服务的核心 API 之一；以指定的登录帐号（用户名或登录别名）和密码进行系统登录操作。
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <param name="password">密码明文</param>
        /// <param name="resultModel">输出参数，用户数据模型</param>
        /// <returns></returns>
        internal static LoginResult UserLogin(string account, string password, out UserInfoModel resultModel)
        {
            resultModel = null;

            if (IsOnline(account) && !Config.EnableMultiDeciveOnline)
            { return LoginResult.InvalidLoginStatus; }

            MemberToken token = new MemberToken(account);
            LoginResult ret = token.Login(password);
            if (ret == LoginResult.Success)
            { resultModel = token.User; }

            return ret;
        }

        /// <summary>
        /// 身份验证登录服务的核心 API 之一；以指定的移动设备硬件编号、登录帐号（用户名或登录别名）和密码进行移动设备登录操作。
        /// </summary>
        /// <param name="imei">移动设备硬件编号</param>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <param name="password">密码明文</param>
        /// <param name="resultModel">输出参数，用户数据模型</param>
        /// <returns></returns>
        internal static LoginResult UserLogin(string imei, string account, string password, out UserInfoModel resultModel)
        {
            resultModel = null;

            if (IsOnline(account) && !Config.EnableMultiDeciveOnline)
            { return LoginResult.InvalidLoginStatus; }

            if (PhoneIsOnline(imei))
            {
                MemberToken memberToken = GetTokenByIMEI(imei);
                if (memberToken != null && (memberToken.Equals(account)))
                {
                    if (!ValidatePassword(account, password))
                    { return LoginResult.InvalidPassword; }

                    memberToken.UpdateLastRequestTime();
                    resultModel = memberToken.User;
                    return LoginResult.Success;
                }

                return LoginResult.InvalidDeviceStatus;
            }

            MemberToken token = new MemberToken(account, imei);
            LoginResult ret = token.Login(password);
            if (ret == LoginResult.Success)
            { resultModel = token.User; }

            return ret;
        }

        /// <summary>
        /// 以指定的登录帐号（用户名或登录别名）和密码进行系统强制登录操作。
        /// <para>强制登录将导致 <paramref name="account"/> 当前在其他设备的登录强制下线。</para>
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <param name="password">密码明文</param>
        /// <param name="resultModel">输出参数，用户数据模型</param>
        /// <returns></returns>
        internal static bool UserForcedLogin(string account, string password, out UserInfoModel resultModel)
        {
            resultModel = null;

            if (IsOnline(account))
            { UserLogout(account); }

            return UserLogin(account, password, out resultModel) == LoginResult.Success;
        }

        /// <summary>
        /// 身份验证登录服务的核心 API 之一；以指定的移动设备硬件编号、登录帐号（用户名或登录别名）和密码进行移动设备强制登录操作。
        /// <para>强制登录将导致 <paramref name="imei"/> 参数所示移动设备及当前登录该设备的用户、或者 <paramref name="account"/> 参数所示用户当前在其他设备的登录强制下线。</para>
        /// </summary>
        /// <param name="imei">移动设备硬件编号</param>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <param name="password">密码明文</param>
        /// <param name="resultModel">输出参数，用户数据模型</param>
        /// <returns></returns>
        internal static bool UserForcedLogin(string imei, string account, string password, out UserInfoModel resultModel)
        {
            resultModel = null;

            if (IsOnline(account))
            { UserLogout(account); }

            if (PhoneIsOnline(imei))
            { PhoneLogout(imei); }

            return UserLogin(imei, account, password, out resultModel) == LoginResult.Success;
        }


        /// <summary>
        /// 身份验证登录服务的核心 API 之一；对指定的登录帐号（用户名或登录别名）执行注销登录操作。
        /// <para>该登录帐号（用户名或登录别名）在所有设备上的登录状态将会改变为离线。</para>
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <returns></returns>
        internal static IEnumerable<KeyValuePair<Guid, LogoutResult>> UserLogout(string account)
        {
            MemberToken[] tokens = GetTokenByAccount(account);
            if (tokens == null || tokens.Length == 0)
            { return Enumerable.Empty<KeyValuePair<Guid, LogoutResult>>().ToArray(); }

            return tokens.Select(item => new KeyValuePair<Guid, LogoutResult>(item.Guid, item.Logout())).ToArray();
        }

        /// <summary>
        /// 身份验证登录服务的核心 API 之一；对指定的登录身份的票据执行注销登录操作。
        /// <para>该登录身份票据值和所对应用户的登录状态将会改变为离线。</para>
        /// </summary>
        /// <param name="tokenGuid">登录身份的票据值。</param>
        /// <returns></returns>
        internal static LogoutResult UserLogout(Guid tokenGuid)
        {
            MemberToken token = GetTokenByGuid(tokenGuid);
            if (token == null)
            { return LogoutResult.InvalidLoginStatus; }

            return token.Logout();
        }


        /// <summary>
        /// 身份验证登录服务的核心 API 之一；按移动设备硬件编号指定移动设备执行注销登录操作。
        /// <para>该移动设备的登录状态及 设备上登录的用户将同时改变为离线。</para>
        /// </summary>
        /// <param name="imei">移动设备硬件编号</param>
        /// <returns></returns>
        internal static LogoutResult PhoneLogout(string imei)
        {
            MemberToken token = GetTokenByIMEI(imei);
            if (token == null)
            { return LogoutResult.InvalidDeviceStatus; }

            return token.Logout();
        }

        #endregion

        #region internal 静态方法定义 - 获取登录状态相关信息

        /// <summary>
        /// 获取用于存储在线用户列表的对象池。
        /// </summary>
        /// <returns></returns>
        internal static MemberTokenCollection GetTokensPool()
        {
            return Tokens;
        }

        /// <summary>
        /// 根据指定的移动设备编号获取当前登录该设备的用户身份信息。
        /// <para>如果该方法返回 null，则说明该移动设备编号无效或者当前时间没有用户使用该编号的移动设备登录。</para>
        /// </summary>
        /// <param name="imei"></param>
        /// <returns></returns>
        public static MemberToken GetTokenByIMEI(string imei)
        {
            return Tokens.GetTokenByIMEI(imei);
        }

        /// <summary>
        /// 根据指定的登录帐号（用户名或登录别名）获取该用户的登录身份信息。
        /// <para>如果该方法返回一个空数组，则说明该用户无效或者尚未登录。</para>
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <returns></returns>
        public static MemberToken[] GetTokenByAccount(string account)
        {
            return Tokens.GetTokenByAccount(account);
        }

        /// <summary>
        /// 根据指定的登录身份信息的 Token KEY 值获取该登陆身份信息。
        /// <para>如果该方法返回 null，则说明该 <paramref name="token"/> 无效。</para>
        /// </summary>
        /// <param name="token">登录身份信息的 Token KEY 值</param>
        /// <returns></returns>
        public static MemberToken GetTokenByGuid(Guid token)
        {
            return Tokens.GetTokenByGuid(token);
        }

        /// <summary>
        /// 根据指定的登录身份信息的 Token KEY 值获取该登陆身份信息。
        /// <para>如果该方法返回 null，则说明该 <paramref name="token"/> 无效。</para>
        /// </summary>
        /// <param name="token">字符串格式的登录身份信息的 Token KEY 值</param>
        /// <returns></returns>
        public static MemberToken GetTokenByGuid(string token)
        {
            return Tokens.GetTokenByGuid(token);
        }


        /// <summary>
        /// 判断指定的登录帐号（用户名或登录别名）是否当前登录在线。
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <returns></returns>
        public static bool IsOnline(string account)
        {
            MemberToken[] tokens = GetTokenByAccount(account);
            return tokens.Any(token => token != null && token.LoginState == LoginState.Online);
        }

        /// <summary>
        /// 根据指定的登录身份 KEY 值信息判断该登录身份是否当前登录在线。
        /// </summary>
        /// <param name="token">登录身份 KEY 值</param>
        /// <returns></returns>
        public static bool IsOnline(Guid token)
        {
            MemberToken key = GetTokenByGuid(token);
            return key != null && key.LoginState == LoginState.Online;
        }

        /// <summary>
        /// 根据移动设备硬件编号判断该移动设备是否当前登录在线。
        /// </summary>
        /// <param name="imei">移动设备硬件编号</param>
        /// <returns></returns>
        public static bool PhoneIsOnline(string imei)
        {
            MemberToken token = GetTokenByIMEI(imei);
            return token != null && token.LoginState == LoginState.Online;
        }

        #endregion

        #region internal 静态方法定义 - 验证登陆状态

        /// <summary>
        /// 验证 <paramref name="tokenKey"/> 指定的登录身份是否有效且在线，并且登录用户为 <paramref name="account"/>。
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <param name="tokenKey">登录身份的Token标识</param>
        /// <returns></returns>
        internal static bool ValidateUserTokenLoginState(string account, Guid tokenKey)
        {
            string temp = account != null ? account.Trim() : string.Empty;
            if (string.IsNullOrWhiteSpace(temp))
            { return false; }

            MemberToken token = GetTokenByGuid(tokenKey);
            return token != null && token.LoginState == LoginState.Online && (token.Equals(temp));
        }

        /// <summary>
        /// 验证 <paramref name="tokenKey"/> 指定的登录身份是否有效且在线，并且为移动设备登录同时设备编号为 <paramref name="imei"/>。
        /// </summary>
        /// <param name="imei">移动设备硬件编号</param>
        /// <param name="tokenKey">登录身份的Token标识</param>
        /// <returns></returns>
        internal static bool ValidatePhoneTokenLoginState(string imei, Guid tokenKey)
        {
            string temp = imei != null ? imei.Trim() : string.Empty;
            if (string.IsNullOrWhiteSpace(temp))
            { return false; }

            MemberToken token = GetTokenByGuid(tokenKey);
            return token != null && token.LoginState == LoginState.Online && (token.IsValidIMEI && token.IsPhoneLogin && token.IMEI == temp);
        }

        /// <summary>
        /// 验证 <paramref name="tokenKey"/> 指定的登录身份是否有效且在线，并且登录用户为 <paramref name="account"/> 同时为移动设备登录同时移动设备登录设备编号为 <paramref name="imei"/>。
        /// </summary>
        /// <param name="account">登录帐号（用户名或登录别名）</param>
        /// <param name="imei">移动设备硬件编号</param>
        /// <param name="tokenKey">登录身份的Token标识</param>
        /// <returns></returns>
        internal static bool ValidateUserPhoneTokenLoginState(string account, string imei, Guid tokenKey)
        {
            string temp1 = account != null ? account.Trim() : string.Empty;
            if (string.IsNullOrWhiteSpace(temp1))
            { return false; }

            string temp2 = imei != null ? imei.Trim() : string.Empty;
            if (string.IsNullOrWhiteSpace(temp2))
            { return false; }

            MemberToken token = GetTokenByGuid(tokenKey);
            return token != null
                && token.LoginState == LoginState.Online
                && (token.Equals(temp1))
                && (token.IsValidIMEI && token.IsPhoneLogin && token.IMEI == temp2);
        }

        #endregion

        #region INotifyPropertyChanged 接口实现

        /// <summary>
        /// 引发 PropertyChanged 事件。
        /// </summary>
        public virtual void OnPropertyChanged()
        {
            this.OnPropertyChanged(_LoginTimeName);
            this.OnPropertyChanged(_LogoutTimeName);
            this.OnPropertyChanged(_LastRequestTimeName);
        }

        /// <summary>
        /// 引发带有提供参数的 PropertyChanged 事件。
        /// </summary>
        /// <param name="propertyName"></param>
        public virtual void OnPropertyChanged(string propertyName)
        {
            if (string.IsNullOrWhiteSpace(propertyName))
            { return; }

            if (this.PropertyChanged != null)
            {
                PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName);
                this.PropertyChanged(this, e);
            }
        }

        /// <summary>
        /// 当 LoginTime、LogoutTime 或 LastRequestTime 属性值变更时触发动作。
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region 运算符重载

        /// <summary>
        /// 判断两个 <see cref="MemberToken"/> 对象是否表示相同的登录身份标识。
        /// </summary>
        /// <param name="item1">要比较的Token</param>
        /// <param name="item2">要比较的Token</param>
        /// <returns></returns>
        public static bool operator ==(MemberToken item1, MemberToken item2)
        {
            if (Object.ReferenceEquals(item1, null))
            { return Object.ReferenceEquals(item2, null); }

            return item1.Equals(item2);
        }

        /// <summary>
        /// 判断两个 <see cref="MemberToken"/> 对象是否表示不同的登录身份标识。
        /// </summary>
        /// <param name="item1">要比较的Token</param>
        /// <param name="item2">要比较的Token</param>
        /// <returns></returns>
        public static bool operator !=(MemberToken item1, MemberToken item2)
        {
            return !(item1 == item2);
        }


        /// <summary>
        /// 确定当前对象是否与指定的另一对象表示相同的登录身份标识。
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (Object.ReferenceEquals(obj, null))
            { return false; }

            if (obj is MemberToken)
            { return this.Equals(obj as MemberToken); }
            else if (obj is string)
            { return this.Equals(obj as string); }
            else if (obj is Guid)
            { return this.Equals((Guid)obj); }
            else
            { return base.Equals(obj); }
        }

        /// <summary>
        /// 确定当前对象是否与指定的另一 <see cref="MemberToken"/> 对象表示相同的登录身份标识。
        /// </summary>
        /// <param name="other">Token</param>
        /// <returns></returns>
        public bool Equals(MemberToken other)
        {
            if (Object.ReferenceEquals(this, other))
            { return true; }

            if (Object.ReferenceEquals(other, null))
            { return false; }

            return this.Equals(other.Guid) && this.Equals(other.UserName);
        }

        /// <summary>
        /// 确定当前对象是否与指定的另一字符串所示的 <see cref="MemberToken"/> 对象表示相同的登录身份标识。
        /// </summary>
        /// <param name="account">用户名 或 登录别名</param>
        /// <returns></returns>
        public bool Equals(string account)
        {
            if (string.IsNullOrWhiteSpace(account))
            { return false; }

            return (this.UserName == account || this.LoginCode == account);
        }

        /// <summary>
        /// 确定当前对象是否与指定的另一 <see cref="System.Guid"/> 所示的 <see cref="MemberToken"/> 对象表示相同的登录身份标识。
        /// </summary>
        /// <param name="other">Token的guid</param>
        /// <returns></returns>
        public bool Equals(Guid other)
        {
            return this.Guid == other;
        }


        /// <summary>
        /// 返回此实例的哈希代码，重写自 Object.GetHashCode。
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Guid.GetHashCode();
        }

        #endregion
    }
}
